const _= require('lodash');
const rsa= require('./rsa');
const crypto= require('crypto');
const initBlock={
    index: 0,
    prevHash: '0',
    timestamp: 1558448469612,
    data: 'Hello MyBlockChain',
    nonce: 12863166,
    hash: '0000007d1ce31dd30afe046ecaf1f1c8c714d6a796bf70a5f1de5b4456a0a262'
}

class Blockchain{
    constructor(){
        this.blockchain= [initBlock];
        this.data=[];
        this.dificulty=4;
        this.init();
    }
    init(){
        this.bindExit();
    }
    bindExit(){
        process.on('exit', ()=>{
            console.log('网络一线牵，珍惜这段缘')
        })
    }
    getLastBlock(){
        return this.blockchain[this.blockchain.length-1];
    }
    transfer(from, to, amount){
        //首先检查余额够不够，如果是挖矿则不检查
        if(from && this.balance(from)< amount){
            console.log('余额不足')
            return;
        }
        //然后对交易进行签名
        const trans= {from, to, amount};
        trans.sign= rsa.sign(trans);
        this.data.push(trans);
        return trans;
    }
    isValidTransfer(transfer){
        //使用公钥校验，转出地址就是公钥
        const pub= transfer.from;
        return rsa.verify(transfer, pub);
    }
    mine(addr){
        //使用 isValidTransfer() 筛选出合法交易（不仅可以校验自己，也可能是在校验别人的转款记录）
        this.data= _.filter(this.data, trance=> this.isValidTransfer(trance));
        //挖矿奖励
        this.transfer(0, rsa.pub, 100);
        const newBlock= this.generateNewBlock();
        if(this.isValidBlock(newBlock) && this.isValidChain(this.blockchain)){
            this.blockchain.push(newBlock)
            this.data=[];   //至关重要
            return newBlock;
        }
        else
            console.log('invalid block', newBlock);
    }
    generateNewBlock(){
        const lastBlock= this.getLastBlock();
        const index= lastBlock.index+1;
        const prevHash= lastBlock.hash;
        const timestamp= new Date().getTime();
        const data= this.data;
        let nonce= 0;
        let hash='';
        const zero='0'.repeat(this.dificulty);
        while(hash.slice(0, this.dificulty)!== zero){
            nonce++;    //必须先加一，后算 hash
            hash= this.computeHash(index, prevHash, timestamp, data, nonce);
        }
        return {
            index,
            prevHash,
            timestamp,
            data,
            nonce,
            hash,
        }
    }
    computeHash(index, prevHash, timestamp, data, nonce){
        //这个hash的生成过程有问题，以后再解决：
        // 1、字符串的拼接顺序是怎么确定的？
        // 2、如果data是多个对象组成的数组，这样简单的相加是不行的！
        return crypto.createHash('sha256')
            .update(index+ prevHash+ timestamp+ data+ nonce)
            .digest('hex')
    }
    isValidBlock(block, prevblock=this.getLastBlock()){
        return (
            block.index === prevblock.index+1 &&
            block.prevHash === prevblock.hash &&
            block.timestamp > prevblock.timestamp &&
            block.hash === this.computeHash(block.index, block.prevHash, block.timestamp, block.data, block.nonce)
            //这里需要进行工作量证明的校验
            //还需要对每笔交易记录进行校验
        )
    }
    isValidChain(chain= this.blockchain){
        let n= chain.length;
        for(let i= n-1; i>0; i--){
            if(!this.isValidBlock(chain[i], chain[i-1])){
                console.log('错误块：', chain[i]);
                return false;
            }
        }
        //这里需要进行创世区块的校验（待续）
        return true;
    }
    balance(addr){
        let sum= 0;
        this.blockchain.forEach(block=>{
            if(!Array.isArray(block.data))
                return;
            block.data.forEach(trans=>{
                if(trans.from === addr)
                    sum-= trans.amount;
                if(trans.to === addr)
                    sum+= trans.amount;
            })
        });
        return sum;
    }

}

module.exports= Blockchain;

// bc= new Blockchain()
// console.log('udp', bc.udp)